/*
 * Copyright ©2015-2021 Jaemon. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gitee.jaemon.spring.adorner.generator;

import io.gitee.jaemon.spring.adorner.AdornerConstants;
import io.gitee.jaemon.spring.adorner.core.AdornerApplicationContext;

import io.gitee.jaemon.spring.adorner.core.ApiParamShow;
import io.gitee.jaemon.spring.adorner.core.IndexConvert;
import io.gitee.jaemon.spring.adorner.type.ApiDefinition;
import io.gitee.jaemon.spring.adorner.type.CommonParameter;
import io.gitee.jaemon.spring.adorner.type.ParamPair;
import io.gitee.jaemon.spring.adorner.util.LoggerHelper;
import io.gitee.jaemon.spring.adorner.util.StringUtils;

import java.lang.reflect.Method;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import static io.gitee.jaemon.spring.adorner.AdornerConstants.*;
import static io.gitee.jaemon.spring.adorner.util.AdornerUtils.paramTable;
import static io.gitee.jaemon.spring.adorner.core.AdornerProperty.INSTANCE;

/**
 * markdown格式freemark模板方式文档生成器
 *
 * @author Jaemon
 * @since 1.0
 */
public class MarkdownFreemarkGenerator extends AbstractFreemarkGenerator {
    @Override
    public Map<String, Object> dataModel(List<ApiDefinition<ParamPair>> apiDefinitions, AdornerApplicationContext context) {
        Map<String, Object> dataModel = new HashMap<>();

        dataModel.put("group", context.group());
        dataModel.put("simplify", context.simplify());
        dataModel.put("AdDateTime", LocalDate.now().format(AdornerConstants.DATE_FORMAT));

        dataModel.put("requestUrlTitle", INSTANCE.get(SPRING_ADORNER_API_REQUEST_URL));
        dataModel.put("requestMethodTitle", INSTANCE.get(SPRING_ADORNER_API_REQUEST_METHOD));
        dataModel.put("requestParamTitle", INSTANCE.get(SPRING_ADORNER_API_REQUEST_PARAM));
        dataModel.put("requestExampleTitle", INSTANCE.get(SPRING_ADORNER_API_REQUEST_EXAMPLE));
        dataModel.put("responseParamTitle", INSTANCE.get(SPRING_ADORNER_API_RESPONSE_PARAM));
        dataModel.put("responseExampleTitle", INSTANCE.get(SPRING_ADORNER_API_RESPONSE_EXAMPLE));

        head(dataModel, apiDefinitions, context);
        body(dataModel, apiDefinitions, context);

        return dataModel;
    }


    /**
     * 头部填充域
     *
     * @param dataModel
     *          ftl填充数据
     * @param apiDefinitions
     *          接口定义集合 {@link ApiDefinition}
     * @param context
     *          上下文对象
     */
    protected void head(Map<String, Object> dataModel, List<ApiDefinition<ParamPair>> apiDefinitions, AdornerApplicationContext context) {
        CommonParameter commonParameter = context.commonParameter();
        if (commonParameter != null) {
            Class<?> request = commonParameter.request();
            String requestTable = paramTable(request);
            if (StringUtils.isNotEmpty(requestTable)) {
                dataModel.put("commonRequestTable", requestTable);
            }

            Class<?> response = commonParameter.response();
            String responseTable = paramTable(response);
            if (StringUtils.isNotEmpty(responseTable)) {
                dataModel.put("commonResponseTable", responseTable);
            }
        }
    }

    /**
     * @param dataModel
     *          ftl填充数据
     * @param apiDefinitions
     *          接口定义集合 {@link ApiDefinition}
     * @param context
     *          上下文对象
     */
    protected void body(Map<String, Object> dataModel, List<ApiDefinition<ParamPair>> apiDefinitions, AdornerApplicationContext context) {
        Map<String, List<ApiDefinition<ParamPair>>> apiDefinitionsGroup =
                apiDefinitions.stream().collect(
                        Collectors.groupingBy(e -> e.getGroupName(), LinkedHashMap::new, Collectors.toList())
                );

        IndexConvert convert = context.indexConvert();
        int groupIndex = 0, apiIndex = 0;
        Map<String, Object> api;
        List<Map<String, Object>> apiModels = new ArrayList<>();
        dataModel.put("apis", apiModels);

        for (
                Map.Entry<String, List<ApiDefinition<ParamPair>>> ads : apiDefinitionsGroup.entrySet()
        ) {
            // 分组标题
            String groupTitle = null;
            if (context.group()) {
                groupIndex++;
                groupTitle = convert.group(groupIndex) + ads.getKey();
            }

            List<ApiDefinition<ParamPair>> apis = ads.getValue();
            for (
                    int i = 0; i < apis.size(); i++
            ) {
                api = new HashMap<>();
                ApiDefinition<ParamPair> apiDefinition = apis.get(i);
                apiIndex++;
                int index = context.group()? i + 1 : apiIndex;
                if (i == 0) {
                    api.put("groupTitle", groupTitle);
                }

                // 接口标题
                String apiTitle = convert.api(groupIndex, index) +
                        apiName(
                                apiDefinition.getClazz(), apiDefinition.getMethod(), apiDefinition.getApiName()
                        );
                api.put("apiTitle", apiTitle);

                api.put("apiDefaultRequestUrl", apiDefinition.getDefaultUrl());
                api.put("apiRequestUrls", apiDefinition.getUrls());

                String requestMethod;
                if (context.simplify()) {
                    requestMethod = apiDefinition.getDefaultRequestMethod().name();
                } else {
                    requestMethod = Arrays.stream(apiDefinition.getRequestMethods()).map(e -> e.name()).collect(Collectors.joining(COMMA));
                }
                api.put("apiRequestMethod", requestMethod);
                api.put("apiDefaultRequestMethod", apiDefinition.getDefaultRequestMethod());

                ApiParamShow apiParamShow = context.apiParamShow();
                // 请求参数展示
                String request = apiParamShow.request(apiDefinition.getRequest());
                if (StringUtils.isNotEmpty(request)) {
                    // 请求数据
                    api.put("apiRequestParam", request);
                }

                // 响应参数展示
                String response = apiParamShow.response(apiDefinition.getResponse());
                if (StringUtils.isNotEmpty(response)) {
                    // 响应数据
                    api.put("apiResponseParam", response);
                }

                apiModels.add(api);

                LoggerHelper.debug("Generate API=[%s.%s()] interface doc content.",
                        apiDefinition.getClazz().getName(), apiDefinition.getMethod().getName());
            }

        }
    }

    private String apiName(Class clazz, Method method, String apiName) {
        boolean exists =
                clazz.isAnnotationPresent(Deprecated.class) ||
                        method.isAnnotationPresent(Deprecated.class);

        return exists ? String.format("~~%s~~", apiName) : apiName;
    }
}